#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/ide.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/gpio.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of_gpio.h>
#include <linux/semaphore.h>
#include <linux/timer.h>
#include <linux/i2c.h>
#include <asm/mach/map.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include "mpu6050.h"

#define MPU6050_CNT    1
#define MPU6050_NAME   "mpu6050"
 
struct mpu6050_dev {
    dev_t devid;                    /* 设备号   */
    struct cdev cdev;               /* cdev    */
    struct class *class;            /* 类      */
    struct device *device;          /* 设备    */
    struct device_node    *nd;      /* 设备节点 */
    int major;                      /* 主设备号 */
    struct i2c_client *i2cx;         /* 私有数据 */

    short a[3], g[3], tem;          /* 三个传感器数据 角速度、角度、温度*/
    uint8_t name;
};
 
static struct mpu6050_dev mpu6050dev;
 
/*
 * dev：i2c客户端传入匹配的dev即可
 * reg：要写入的寄存器地址
 * val：要写入的值
 * return 0表示成功其他表示失败
*/
static int mpu6050_write_reg(struct i2c_client *dev, uint8_t reg, uint8_t val)
{
	uint8_t send_buf[2];
    struct i2c_msg msg;
 
    send_buf[0] = reg;                 	/* 寄存器首地址 */
    memcpy(&send_buf[1], &val, 1);      /* 将要写入的数据拷贝到发送缓冲数组里面，如果有多位可以改变函数添加len参数 */
 
    msg.addr = dev->addr;    			/* 设备地址 */
    msg.flags = 0;              		/* 标记为写数据 */
    msg.buf = send_buf;                	/* 要写入的数据缓冲区 */
    msg.len = 2;            			/* 要写入的数据长度：地址位+数据位 */
 
    return i2c_transfer(dev->adapter, &msg, 1);
}

/*
 * dev：i2c客户端传入匹配的dev即可
 * reg：要读取的寄存器地址
 * val：读取到的值
 * return 0表示成功其他表示失败
*/
static int mpu6050_read_reg(struct i2c_client *dev, uint8_t reg, uint8_t *val, uint8_t len)
{
	int ret;
    struct i2c_msg msg[2];
 
    /* msg[0]为发送要读取的首地址 */
    msg[0].addr = dev->addr;         	/* 设备地址 */
    msg[0].flags = 0;                   /* 标记为发送数据 */
    msg[0].buf = &reg;                  /* 读取的首地址 */
    msg[0].len = 1;                     /* reg长度*/
 
    /* msg[1]读取数据 */
    msg[1].addr = dev->addr;         	/* 设备地址 */
    msg[1].flags = I2C_M_RD;            /* 标记为读取数据*/
    msg[1].buf = val;                   /* 读取数据缓冲区 */
    msg[1].len = len;                   /* 要读取的数据长度*/
 
    ret = i2c_transfer(dev->adapter, msg, len + 1);
    if(ret == (len+1)) {
        ret = 0;
    }else {
        printk("i2c_cline: 0x%02X read failed=%d reg=%06x len= %d\n",dev->addr, ret, reg, len);
        ret = -EREMOTEIO;
    }
    return ret;
}

/*
 * @description    : 读取mpu6050的数据，读取原始数据， 注意！ 
 * @return         : 无。
 */
void mpu6050_readdata(struct mpu6050_dev *dev)
{
    unsigned char buf[14],name[1];
    mpu6050_read_reg(dev->i2cx, MPU6050_ACCEL_XOUT_H, buf, 14);  

    dev->a[0] = (short)(buf[0]<<8 | buf[1]);
    dev->a[1] = (short)(buf[2]<<8 | buf[3]);
    dev->a[2] = (short)(buf[4]<<8 | buf[5]);

    dev->tem = (short)(buf[6]<<8 | buf[7]);

    dev->g[0] = (short)(buf[8]<<8 | buf[9]);
    dev->g[1] = (short)(buf[10]<<8 | buf[11]);
    dev->g[2] = (short)(buf[12]<<8 | buf[13]);

    mpu6050_read_reg(dev->i2cx, MPU6050_WHO_AM_I, name, 1);
    dev->name = name[0];
}
 
 
 
/*
 * @description        : 打开设备
 * @param - inode     : 传递给驱动的inode
 * @param - filp     : 设备文件，file结构体有个叫做private_data的成员变量
 *                       一般在open的时候将private_data指向设备结构体。
 * @return             : 0 成功;其他 失败
 */
static int mpu6050_open(struct inode *inode, struct file *filp)
{
    uint8_t value;
    filp->private_data = &mpu6050dev;
    /* 初始化mpu6050 */
    mpu6050_write_reg(mpu6050dev.i2cx,MPU6050_PWR_MGMT_1, 0x80);
    mdelay(100);
    mpu6050_write_reg(mpu6050dev.i2cx,MPU6050_PWR_MGMT_1, 0x00);
    mpu6050_write_reg(mpu6050dev.i2cx,MPU6050_SMPLRT_DIV, 0x07);    
    mpu6050_write_reg(mpu6050dev.i2cx,MPU6050_CONFIG, 0x06);
    mpu6050_write_reg(mpu6050dev.i2cx,MPU6050_ACCEL_CONFIG, 0x08);//+-4g
    mpu6050_write_reg(mpu6050dev.i2cx,MPU6050_GYRO_CONFIG, 0x08);//+-500/s
    mpu6050_write_reg(mpu6050dev.i2cx,MPU_FIFO_EN_REG, 0x00);    
    mpu6050_write_reg(mpu6050dev.i2cx,MPU6050_INT_ENABLE, 0x00);    

    mpu6050_write_reg(mpu6050dev.i2cx,MPU6050_PWR_MGMT_1, 0x01);
    mpu6050_write_reg(mpu6050dev.i2cx,MPU6050_PWR_MGMT_2, 0x00);
 
    mpu6050_read_reg(mpu6050dev.i2cx, MPU6050_WHO_AM_I, &value, 1);
    printk("mpu6050 ID = %#X\r\n", value);    

    return 0;
}
 
/*
 * @description        : 从设备读取数据 
 * @param - filp     : 要打开的设备文件(文件描述符)
 * @param - buf     : 返回给用户空间的数据缓冲区
 * @param - cnt     : 要读取的数据长度
 * @param - offt     : 相对于文件首地址的偏移
 * @return             : 读取的字节数，如果为负值，表示读取失败
*/
static ssize_t mpu6050_read(struct file *filp, char __user *buf, size_t cnt, loff_t *off)
{
    short data[7];
    long err = 0;
 
    struct mpu6050_dev *dev = (struct mpu6050_dev *)filp->private_data;
    mpu6050_readdata(dev);

    data[0] = dev->a[0];
    data[1] = dev->a[1];
    data[2] = dev->a[2];
    data[3] = dev->tem;
    data[4] = dev->g[0];
    data[5] = dev->g[1];
    data[6] = dev->g[2];
 
    err = copy_to_user(buf, data, sizeof(data));
    return 0;
}
 
/*
 * @description        : 关闭/释放设备
 * @param - filp     : 要关闭的设备文件(文件描述符)
 * @return             : 0 成功;其他 失败
 */
 
static int mpu6050_release(struct inode *inode, struct file *filp)
{
    return 0;
}

 
/* mpu6050操作函数 */
static const struct file_operations mpu6050_ops = {
    .owner = THIS_MODULE,
    .open = mpu6050_open,
    .read = mpu6050_read,
    .release = mpu6050_release,
};
 
 /*
  * @description     : i2c驱动的probe函数，当驱动与
  *                    设备匹配以后此函数就会执行
  * @param - client  : i2c设备
  * @param - id      : i2c设备ID
  * @return          : 0，成功;其他负值,失败
*/
static int mpu6050_probe(struct i2c_client *client, const struct i2c_device_id *id)
{
    /* 1、构建设备号 */
    if (mpu6050dev.major) {
        mpu6050dev.devid = MKDEV(mpu6050dev.major, 0);
        register_chrdev_region(mpu6050dev.devid, MPU6050_CNT, MPU6050_NAME);
    } else {
        alloc_chrdev_region(&mpu6050dev.devid, 0, MPU6050_CNT, MPU6050_NAME);
        mpu6050dev.major = MAJOR(mpu6050dev.devid);
    }
 
    /* 2、注册设备 */
    cdev_init(&mpu6050dev.cdev, &mpu6050_ops);
    cdev_add(&mpu6050dev.cdev, mpu6050dev.devid, MPU6050_CNT);
 
    /* 3、创建类 */
    mpu6050dev.class = class_create(THIS_MODULE, MPU6050_NAME);
    if (IS_ERR(mpu6050dev.class)) {
        return PTR_ERR(mpu6050dev.class);
    }

    /* 4、创建设备 */
    mpu6050dev.device = device_create(mpu6050dev.class, NULL, mpu6050dev.devid, NULL, MPU6050_NAME); // 默认使用
    if (IS_ERR(mpu6050dev.device)) {
        return PTR_ERR(mpu6050dev.device);
    }

    mpu6050dev.i2cx = client;

    return 0;
}
 
/*
 * @description     : i2c驱动的remove函数，移除i2c驱动的时候此函数会执行
 * @param - client     : i2c设备
 * @return          : 0，成功;其他负值,失败
 */
static int mpu6050_remove(struct i2c_client *client)
{
    /* 删除设备 */
    cdev_del(&mpu6050dev.cdev);
    unregister_chrdev_region(mpu6050dev.devid, MPU6050_CNT);
    /* 注销掉类和设备 */
    device_destroy(mpu6050dev.class, mpu6050dev.devid);
    class_destroy(mpu6050dev.class);
    return 0;
}

/* 传统匹配方式ID列表 */
static const struct i2c_device_id mpu6050_id[] = {
    {"device_tree, mpu6050", 0},  
};
 
/* 设备树匹配列表 */
static const struct of_device_id mpu6050_of_match[] = {
    { .compatible = "device, mpu6050" },
};
 
/* i2c驱动结构体 */    
static struct i2c_driver mpu6050_driver = {
    .probe = mpu6050_probe,
    .remove = mpu6050_remove,
    .driver = {
        .owner = THIS_MODULE,
        .name = "mpu6050",
        .of_match_table = mpu6050_of_match, 
    },
    .id_table = mpu6050_id,
};
 
/*
 * @description    : 驱动入口函数
 * @param         : 无
 * @return         : 无
 */
static int __init mpu6050_init(void)
{
    int ret = 0;
    ret = i2c_add_driver(&mpu6050_driver);
    return ret;
}
 
/*
 * @description    : 驱动出口函数
 * @param         : 无
 * @return         : 无
 */
static void __exit mpu6050_exit(void)
{
    i2c_del_driver(&mpu6050_driver);
}

module_init(mpu6050_init);
module_exit(mpu6050_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("kongjun");
 
 